package com.learn.english.controller;

import com.learn.english.constants.CacheConstants;
import com.learn.english.domain.R;
import com.learn.english.enums.TimeEnum;
import com.learn.english.model.entity.CourseVideo;
import com.learn.english.service.IVideoService;
import com.learn.english.service.RedisService;
import com.learn.english.utils.StreamGobbler;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.io.FileSystemResource;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.multipart.MultipartFile;

import java.io.File;
import java.math.BigDecimal;
import java.util.*;

/**
 * @author TISNIW
 * @version 1.0
 * @date 2025/6/21 22:34
 * @desc
 */
@RestController
@Slf4j
@RequestMapping("/video")
public class VideoController {
    @Value("${path.user.video}")
    private String HLS_PATH;
    private static final String FFMPEG_PATH = "ffmpeg.exe";
    @Autowired
    private IVideoService videoService;
    @Autowired
    private RedisService redisService;
    @GetMapping("/play/{filename:.+\\.m3u8}")
    public ResponseEntity<FileSystemResource> serveM3U8(@PathVariable String filename) {
        log.info("filename: {}", filename);
        log.info("HLS_PATH: {}", HLS_PATH);
        String[] parts = filename.split("_");
        StringBuffer filePath = new StringBuffer(HLS_PATH);
        for (int i = 0; i < parts.length - 1; i++) {
            filePath.append("/").append(parts[i]);
        }
        redisService.setCacheObject(CacheConstants.CACHE_KEY_VIDEO_PATH  + parts[parts.length - 1], filePath.toString(), TimeEnum.FOUR_HOUR);
        log.info("filePath: {}", filePath);
        filePath.append("/" + parts[parts.length - 1]);

        File file = new File(filePath.toString());
        log.info("filename: {}", file.getAbsolutePath());
        if (!file.exists()) {
            return ResponseEntity.notFound().build();
        }
        videoService.playVideo(filename.replaceFirst(".m3u8$", ""));
        return ResponseEntity.ok()
                .contentType(MediaType.APPLICATION_OCTET_STREAM)
                .header("Access-Control-Allow-Origin", "*")
                .body(new FileSystemResource(file));
    }

    /**
     * 根据章节ID获取视频列表
     * @param chapterId 章节ID
     * @return 视频列表
     */
    @GetMapping("/list")
    public R<List<CourseVideo>> getVideoList(@RequestParam Long chapterId) {
        try {
            List<CourseVideo> videos = videoService.getVideoListByChapterId(chapterId);
            return R.ok(videos);
        } catch (Exception e) {
            log.error("获取视频列表失败", e);
            return R.fail(500, "获取视频列表失败：" + e.getMessage());
        }
    }

    /**
     * 删除视频
     * @param id 视频ID
     * @return 删除结果
     */
    @DeleteMapping("/delete/{id}")
    public R<Boolean> deleteVideo(@PathVariable Long id) {
        try {
            // 先获取视频信息，用于删除文件
            CourseVideo video = videoService.getVideoById(id);
            if (video == null) {
                return R.fail(400, "视频不存在");
            }
            
            // 删除数据库记录
            boolean result = videoService.deleteVideo(id);
            
            if (result) {
                // 这里可以添加删除视频文件的逻辑
                String videoCode = video.getVideoUrl();
                if (videoCode != null) {
                    // 构造文件路径并删除
                    String[] parts = videoCode.split("_");
                    if (parts.length >= 4) {
                        String userId = parts[0];
                        String courseId = parts[1];
                        String chapterId = parts[2];
                        
                        String videoDirPath = HLS_PATH + File.separator + userId + File.separator + courseId + File.separator + chapterId;
                        // 可以异步删除文件以提高响应速度
                        // deleteDirectory(new File(videoDirPath));
                    }
                }
                return R.ok(true);
            } else {
                return R.fail(400, "删除失败");
            }
        } catch (Exception e) {
            log.error("删除视频失败", e);
            return R.fail(500, "删除视频失败：" + e.getMessage());
        }
    }
    
    /**
     * 删除目录及其所有内容
     * @param dir 要删除的目录
     * @return 是否删除成功
     */
    private boolean deleteDirectory(File dir) {
        if (dir.isDirectory()) {
            File[] children = dir.listFiles();
            if (children != null) {
                for (File child : children) {
                    deleteDirectory(child);
                }
            }
        }
        return dir.delete();
    }

    @GetMapping("/play/{filename:.+\\.ts}")
    public ResponseEntity<FileSystemResource> serveTS(@PathVariable String filename) {
        log.info("filename: {}", filename);
        String baseName = filename.replaceFirst("\\d{5}\\.ts$", "");
        String path = redisService.getCacheObject(CacheConstants.CACHE_KEY_VIDEO_PATH + baseName + ".m3u8", String.class);
        File file = new File(path,filename);


        if (!file.exists()) {
            log.warn("TS文件不存在: {}", file.getAbsolutePath());
            return ResponseEntity.notFound().build();
        }

        return ResponseEntity.ok()
                .contentType(MediaType.valueOf("video/mp2t"))
                .header("Access-Control-Allow-Origin", "*")
                .body(new FileSystemResource(file));
    }
    @PutMapping("/update")
    public R<Boolean> updateVideo(@RequestBody CourseVideo courseVideo) {
        try {
            // 检查视频是否存在
            CourseVideo existingVideo = videoService.getVideoById(courseVideo.getId());
            if (existingVideo == null) {
                return R.fail(400, "视频不存在");
            }
            
            // 更新视频信息
            boolean result = videoService.updateVideo(courseVideo);
            if (result) {
                return R.ok();
            } else {
                return R.fail(500, "更新失败");
            }
        } catch (Exception e) {
            log.error("更新视频失败", e);
            return R.fail(500, "更新视频失败：" + e.getMessage());
        }
    }

    @PostMapping("/upload/file")
    public R<String> upload(
            @RequestParam("file") MultipartFile file,
            @RequestParam("courseId") Long courseId,
            @RequestParam(value = "chapterId", required = false) Long chapterId,
            @RequestParam(value = "title", required = false) String title,
            @RequestParam(value = "description", required = false) String description,
            @RequestParam(value = "coverUrl", required = false) String coverUrl,
            @RequestParam(value = "sort", required = false) Integer sort,
            @RequestParam(value = "status", required = false) Integer status,
            @RequestParam(value = "sourceType", required = false) Byte sourceType,
            @RequestParam(value = "playCount", required = false) Long playCount,
            @RequestParam(value = "isFree", required = false) Byte isFree,
            @RequestParam(value = "cost", required = false) BigDecimal cost,
            @RequestParam String userId) {

        if (file.isEmpty()) {
            return R.fail(300, "文件为空");
        }

        String originalFilename = file.getOriginalFilename();
        StringBuilder baseName = new StringBuilder();
        //上传用户id/课程id/章节id/视频编码

        String chapter = chapterId != null ? String.valueOf(chapterId) : "";
        String videoCode = String.valueOf(System.currentTimeMillis() + "course");
        String sql = userId + "_" + courseId + "_" + chapter + "_" + videoCode;
        baseName.append("/")
                .append(userId)
                .append("/")
                .append(courseId)
                .append("/")
                .append(chapter);

        String path = baseName.toString();
        String videoPath = HLS_PATH +baseName;
        baseName.append("/")
                .append(videoCode);
        String m3u8Path = HLS_PATH + baseName;

        try {
            // 创建输出目录
            File dir = new File(videoPath);
            if (!dir.exists()) {
                dir.mkdirs();
            }
            ProcessBuilder pb = new ProcessBuilder("ffmpeg", "-version");
            int exitCode = pb.start().waitFor();
            if (exitCode != 0) {
                throw new RuntimeException("FFmpeg not found in system path.");
            }

            // 保存原始文件
            String inputPath = videoPath + originalFilename;
            file.transferTo(new File(inputPath));

            // 构建 FFmpeg 命令
            List<String> command = new ArrayList<>();
            command.add(FFMPEG_PATH);
            command.add("-i");
            command.add(inputPath);
            command.add("-c:v");
            command.add("h264");
            command.add("-c:a");
            command.add("aac");
            command.add("-start_number"); // 可选，设置 ts 文件名的起始编号
            command.add("0");
            command.add("-hls_time");
            command.add("2"); // 每个 ts 切片时间
            command.add("-hls_list_size");
            command.add("0");

            command.add("-hls_segment_filename");
            command.add(m3u8Path + "%05d.ts");

            command.add("-f");
            command.add("hls");
            command.add(m3u8Path + ".m3u8");
            ProcessBuilder processBuilder = new ProcessBuilder(command);
            final Process process = processBuilder.start();

            // 启动线程读取 FFmpeg 的 stdout 和 stderr（非常重要！）
            StreamGobbler outputGobbler = new StreamGobbler(process.getInputStream(), "OUTPUT", log::info);
            StreamGobbler errorGobbler = new StreamGobbler(process.getErrorStream(), "ERROR", log::error);

            new Thread(outputGobbler).start();
            new Thread(errorGobbler).start();

            log.info("执行 FFmpeg 转码命令");

            // 设置超时机制防止永久阻塞（可选）
            boolean finished = process.waitFor(5, java.util.concurrent.TimeUnit.MINUTES);
            if (!finished) {
                process.destroyForcibly(); // 强制终止
                return R.fail(400, "转码超时");
            }

            exitCode = process.exitValue();
            log.info("转码结果: {}", exitCode);

            if (exitCode != 0) {
                return R.fail(400, "转码失败，退出码：" + exitCode);
            }
            File originalM3u8File = new File(m3u8Path);
            File targetM3u8File = new File(videoPath, sql + ".m3u8");

            boolean renamed = originalM3u8File.renameTo(targetM3u8File);
            if (!renamed) {
                log.warn("无法将 m3u8 文件重命名为 '{}'", sql);
            } else {
                log.info("成功将 m3u8 文件重命名为 '{}'", sql);
            }
            String playbackUrl = "/api/video/" + sql + ".m3u8";
            log.info("播放地址: {}", playbackUrl);
            CourseVideo courseVideo = CourseVideo.builder()
                    .courseId(courseId)
                    .chapterId(chapterId)
                    .title(title)
                    .description(description)
                    .coverUrl(coverUrl)
                    .videoUrl(sql)
                    .sort(sort != null ? sort : 0)
                    .status(status != null ? status : 1)
                    .sourceType(sourceType != null ? sourceType : (byte) 1)
                    .playCount(playCount != null ? playCount : 0)
                    .isFree(isFree != null ? isFree : (byte) 0)
                    .cost(cost != null ? cost : BigDecimal.ZERO)
                    .build();
            courseVideo = videoService.upload(courseVideo);
            return R.ok(playbackUrl);

        } catch (Exception e) {
            e.printStackTrace();
            return R.fail(400, "上传或转码失败：" + e.getMessage());
        }
    }
}